/**
 * Copyright 2020 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.dbfly.mybatis;

import com.jianggujin.dbfly.util.dtd.JDTD;
import com.jianggujin.dbfly.util.dtd.JDTDAttribute;
import com.jianggujin.dbfly.util.dtd.JDTDCardinal;
import com.jianggujin.dbfly.util.dtd.JDTDDecl;
import com.jianggujin.dbfly.util.dtd.JDTDElement;
import com.jianggujin.dbfly.util.dtd.JDTDEmpty;
import com.jianggujin.dbfly.util.dtd.JDTDEnumeration;
import com.jianggujin.dbfly.util.dtd.JDTDItem;
import com.jianggujin.dbfly.util.dtd.JDTDMixed;
import com.jianggujin.dbfly.util.dtd.JDTDName;
import com.jianggujin.dbfly.util.dtd.JDTDPCData;
import com.jianggujin.dbfly.util.dtd.JDTDSequence;

/**
 * JDBFly的Mapper的DTD验证，对Mybatis的DTD进行了扩展
 * 
 * @author jianggujin
 *
 */
public class JDBFlyMapperDTDFactory implements JDBFlyDTDFactory {
    @Override
    public JDTD createDTD() {
        JDTD dtd = new JDTD();
        // <!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* |
        // insert* | update* | delete* | select* )+>
        // <!ATTLIST mapper
        // namespace CDATA #IMPLIED
        // >
        JDTDItem empty = new JDTDEmpty();
        JDTDMixed mapperContent = new JDTDMixed(JDTDCardinal.ONEMANY);
        mapperContent.add("cache-ref", "cache");
        mapperContent.add(JDTDCardinal.ZEROMANY, "resultMap", "parameterMap", "sql", "insert", "update", "delete",
                "select");
        dtd.add(new JDTDElement("mapper", mapperContent, new JDTDAttribute("namespace")));

        // <!ELEMENT cache-ref EMPTY>
        // <!ATTLIST cache-ref
        // namespace CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("cache-ref", empty, new JDTDAttribute("namespace", JDTDDecl.REQUIRED)));

        // <!ELEMENT cache (property*)>
        // <!ATTLIST cache
        // type CDATA #IMPLIED
        // eviction CDATA #IMPLIED
        // flushInterval CDATA #IMPLIED
        // size CDATA #IMPLIED
        // readOnly CDATA #IMPLIED
        // blocking CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("cache", new JDTDName("property", JDTDCardinal.ZEROMANY), "type", "eviction",
                "flushInterval", "size", "readOnly", "blocking"));

        // <!ELEMENT parameterMap (parameter+)?>
        // <!ATTLIST parameterMap
        // id CDATA #REQUIRED
        // type CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("parameterMap", new JDTDMixed(JDTDCardinal.OPTIONAL, JDTDCardinal.ONEMANY, "parameter"),
                new JDTDAttribute("id", JDTDDecl.REQUIRED), new JDTDAttribute("type", JDTDDecl.REQUIRED)));

        // <!ELEMENT parameter EMPTY>
        // <!ATTLIST parameter
        // property CDATA #REQUIRED
        // javaType CDATA #IMPLIED
        // jdbcType CDATA #IMPLIED
        // mode (IN | OUT | INOUT) #IMPLIED
        // resultMap CDATA #IMPLIED
        // scale CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("parameter", empty, new JDTDAttribute("property", "CDATA", JDTDDecl.REQUIRED),
                new JDTDAttribute("javaType"), new JDTDAttribute("jdbcType"),
                new JDTDAttribute("mode", new JDTDEnumeration("IN", "OUT", "INOUT")), new JDTDAttribute("resultMap"),
                new JDTDAttribute("scale"), new JDTDAttribute("typeHandler")));

        // <!ELEMENT resultMap (constructor?,id*,result*,association*,collection*,
        // discriminator?)>
        // <!ATTLIST resultMap
        // id CDATA #REQUIRED
        // type CDATA #REQUIRED
        // extends CDATA #IMPLIED
        // autoMapping (true|false) #IMPLIED
        // >
        dtd.add(new JDTDElement("resultMap", new JDTDSequence(new JDTDName("constructor", JDTDCardinal.OPTIONAL),
                new JDTDName("id", JDTDCardinal.ZEROMANY), new JDTDName("result", JDTDCardinal.ZEROMANY),
                new JDTDName("association", JDTDCardinal.ZEROMANY), new JDTDName("collection", JDTDCardinal.ZEROMANY),
                new JDTDName("discriminator", JDTDCardinal.OPTIONAL)), new JDTDAttribute("id", JDTDDecl.REQUIRED),
                new JDTDAttribute("type", JDTDDecl.REQUIRED), new JDTDAttribute("extends"),
                new JDTDAttribute("autoMapping", new JDTDEnumeration("true", "false"))));

        // <!ELEMENT constructor (idArg*,arg*)>
        dtd.add(new JDTDElement("constructor",
                new JDTDSequence(JDTDCardinal.NONE, JDTDCardinal.ZEROMANY, "idArg", "arg")));

        // <!ELEMENT id EMPTY>
        // <!ATTLIST id
        // property CDATA #IMPLIED
        // javaType CDATA #IMPLIED
        // column CDATA #IMPLIED
        // jdbcType CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("id", empty, "property", "javaType", "column", "jdbcType", "typeHandler"));

        // <!ELEMENT result EMPTY>
        // <!ATTLIST result
        // property CDATA #IMPLIED
        // javaType CDATA #IMPLIED
        // column CDATA #IMPLIED
        // jdbcType CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("result", empty, "property", "javaType", "column", "jdbcType", "typeHandler"));

        // <!ELEMENT idArg EMPTY>
        // <!ATTLIST idArg
        // javaType CDATA #IMPLIED
        // column CDATA #IMPLIED
        // jdbcType CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // select CDATA #IMPLIED
        // resultMap CDATA #IMPLIED
        // name CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("idArg", empty, "javaType", "column", "jdbcType", "typeHandler", "select", "resultMap",
                "name"));

        // <!ELEMENT arg EMPTY>
        // <!ATTLIST arg
        // javaType CDATA #IMPLIED
        // column CDATA #IMPLIED
        // jdbcType CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // select CDATA #IMPLIED
        // resultMap CDATA #IMPLIED
        // name CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("arg", empty, "javaType", "column", "jdbcType", "typeHandler", "select", "resultMap",
                "name"));

        // <!ELEMENT collection (constructor?,id*,result*,association*,collection*,
        // discriminator?)>
        // <!ATTLIST collection
        // property CDATA #REQUIRED
        // column CDATA #IMPLIED
        // javaType CDATA #IMPLIED
        // ofType CDATA #IMPLIED
        // jdbcType CDATA #IMPLIED
        // select CDATA #IMPLIED
        // resultMap CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // notNullColumn CDATA #IMPLIED
        // columnPrefix CDATA #IMPLIED
        // resultSet CDATA #IMPLIED
        // foreignColumn CDATA #IMPLIED
        // autoMapping (true|false) #IMPLIED
        // fetchType (lazy|eager) #IMPLIED
        // >
        dtd.add(new JDTDElement("collection", new JDTDSequence(new JDTDName("constructor", JDTDCardinal.OPTIONAL),
                new JDTDName("id", JDTDCardinal.ZEROMANY), new JDTDName("result", JDTDCardinal.ZEROMANY),
                new JDTDName("association", JDTDCardinal.ZEROMANY), new JDTDName("collection", JDTDCardinal.ZEROMANY),
                new JDTDName("discriminator", JDTDCardinal.OPTIONAL)), new JDTDAttribute("property", JDTDDecl.REQUIRED),
                new JDTDAttribute("column"), new JDTDAttribute("javaType"), new JDTDAttribute("ofType"),
                new JDTDAttribute("jdbcType"), new JDTDAttribute("select"), new JDTDAttribute("resultMap"),
                new JDTDAttribute("typeHandler"), new JDTDAttribute("notNullColumn"), new JDTDAttribute("columnPrefix"),
                new JDTDAttribute("resultSet"), new JDTDAttribute("foreignColumn"),
                new JDTDAttribute("autoMapping", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("fetchType", new JDTDEnumeration("lazy", "eager"))));

        // <!ELEMENT association (constructor?,id*,result*,association*,collection*,
        // discriminator?)>
        // <!ATTLIST association
        // property CDATA #REQUIRED
        // column CDATA #IMPLIED
        // javaType CDATA #IMPLIED
        // jdbcType CDATA #IMPLIED
        // select CDATA #IMPLIED
        // resultMap CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // notNullColumn CDATA #IMPLIED
        // columnPrefix CDATA #IMPLIED
        // resultSet CDATA #IMPLIED
        // foreignColumn CDATA #IMPLIED
        // autoMapping (true|false) #IMPLIED
        // fetchType (lazy|eager) #IMPLIED
        // >
        dtd.add(new JDTDElement("association", new JDTDSequence(new JDTDName("constructor", JDTDCardinal.OPTIONAL),
                new JDTDName("id", JDTDCardinal.ZEROMANY), new JDTDName("result", JDTDCardinal.ZEROMANY),
                new JDTDName("association", JDTDCardinal.ZEROMANY), new JDTDName("collection", JDTDCardinal.ZEROMANY),
                new JDTDName("discriminator", JDTDCardinal.OPTIONAL)), new JDTDAttribute("property", JDTDDecl.REQUIRED),
                new JDTDAttribute("column"), new JDTDAttribute("javaType"), new JDTDAttribute("jdbcType"),
                new JDTDAttribute("select"), new JDTDAttribute("resultMap"), new JDTDAttribute("typeHandler"),
                new JDTDAttribute("notNullColumn"), new JDTDAttribute("columnPrefix"), new JDTDAttribute("resultSet"),
                new JDTDAttribute("foreignColumn"),
                new JDTDAttribute("autoMapping", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("fetchType", new JDTDEnumeration("lazy", "eager"))));

        // <!ELEMENT discriminator (case+)>
        // <!ATTLIST discriminator
        // column CDATA #IMPLIED
        // javaType CDATA #REQUIRED
        // jdbcType CDATA #IMPLIED
        // typeHandler CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("discriminator", new JDTDName("case", JDTDCardinal.ONEMANY),
                new JDTDAttribute("column"), new JDTDAttribute("javaType", JDTDDecl.REQUIRED),
                new JDTDAttribute("jdbcType"), new JDTDAttribute("typeHandler")));

        // <!ELEMENT case (constructor?,id*,result*,association*,collection*,
        // discriminator?)>
        // <!ATTLIST case
        // value CDATA #REQUIRED
        // resultMap CDATA #IMPLIED
        // resultType CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("case", new JDTDSequence(new JDTDName("constructor", JDTDCardinal.OPTIONAL),
                new JDTDName("id", JDTDCardinal.ZEROMANY), new JDTDName("result", JDTDCardinal.ZEROMANY),
                new JDTDName("association", JDTDCardinal.ZEROMANY), new JDTDName("collection", JDTDCardinal.ZEROMANY),
                new JDTDName("discriminator", JDTDCardinal.OPTIONAL)), new JDTDAttribute("value", JDTDDecl.REQUIRED),
                new JDTDAttribute("resultMap"), new JDTDAttribute("resultType")));

        // <!ELEMENT property EMPTY>
        // <!ATTLIST property
        // name CDATA #REQUIRED
        // value CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("property", empty, new JDTDAttribute("name", JDTDDecl.REQUIRED),
                new JDTDAttribute("value", JDTDDecl.REQUIRED)));

        // <!ELEMENT typeAlias EMPTY>
        // <!ATTLIST typeAlias
        // alias CDATA #REQUIRED
        // type CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("typeAlias", empty, new JDTDAttribute("alias", JDTDDecl.REQUIRED),
                new JDTDAttribute("type", JDTDDecl.REQUIRED)));

        // <!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose |
        // if | bind)*>
        // <!ATTLIST select
        // id CDATA #REQUIRED
        // parameterMap CDATA #IMPLIED
        // parameterType CDATA #IMPLIED
        // resultMap CDATA #IMPLIED
        // resultType CDATA #IMPLIED
        // resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
        // statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
        // fetchSize CDATA #IMPLIED
        // timeout CDATA #IMPLIED
        // flushCache (true|false) #IMPLIED
        // useCache (true|false) #IMPLIED
        // databaseId CDATA #IMPLIED
        // lang CDATA #IMPLIED
        // resultOrdered (true|false) #IMPLIED
        // resultSets CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("select",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("id", JDTDDecl.REQUIRED), new JDTDAttribute("parameterMap"),
                new JDTDAttribute("parameterType"), new JDTDAttribute("resultMap"), new JDTDAttribute("resultType"),
                new JDTDAttribute("resultSetType",
                        new JDTDEnumeration("FORWARD_ONLY", "SCROLL_INSENSITIVE", "SCROLL_SENSITIVE")),
                new JDTDAttribute("statementType", new JDTDEnumeration("STATEMENT", "PREPARED", "CALLABLE")),
                new JDTDAttribute("fetchSize"), new JDTDAttribute("timeout"),
                new JDTDAttribute("flushCache", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("useCache", new JDTDEnumeration("true", "false")), new JDTDAttribute("databaseId"),
                new JDTDAttribute("lang"), new JDTDAttribute("resultOrdered", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("resultSets")));

        // <!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set |
        // foreach | choose | if | bind)*>
        // <!ATTLIST insert
        // id CDATA #REQUIRED
        // parameterMap CDATA #IMPLIED
        // parameterType CDATA #IMPLIED
        // timeout CDATA #IMPLIED
        // flushCache (true|false) #IMPLIED
        // statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
        // keyProperty CDATA #IMPLIED
        // useGeneratedKeys (true|false) #IMPLIED
        // keyColumn CDATA #IMPLIED
        // databaseId CDATA #IMPLIED
        // lang CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("insert",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("selectKey"),
                        new JDTDName("include"), new JDTDName("trim"), new JDTDName("where"), new JDTDName("set"),
                        new JDTDName("foreach"), new JDTDName("choose"), new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("id", JDTDDecl.REQUIRED), new JDTDAttribute("parameterMap"),
                new JDTDAttribute("parameterType"), new JDTDAttribute("timeout"),
                new JDTDAttribute("flushCache", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("statementType", new JDTDEnumeration("STATEMENT", "PREPARED", "CALLABLE")),
                new JDTDAttribute("keyProperty"),
                new JDTDAttribute("useGeneratedKeys", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("keyColumn"), new JDTDAttribute("databaseId"), new JDTDAttribute("lang")));

        // <!ELEMENT selectKey (#PCDATA | include | trim | where | set | foreach |
        // choose | if | bind)*>
        // <!ATTLIST selectKey
        // resultType CDATA #IMPLIED
        // statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
        // keyProperty CDATA #IMPLIED
        // keyColumn CDATA #IMPLIED
        // order (BEFORE|AFTER) #IMPLIED
        // databaseId CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("selectKey",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("resultType"),
                new JDTDAttribute("statementType", new JDTDEnumeration("STATEMENT", "PREPARED", "CALLABLE")),
                new JDTDAttribute("keyProperty"), new JDTDAttribute("keyColumn"),
                new JDTDAttribute("order", new JDTDEnumeration("BEFORE", "AFTER")), new JDTDAttribute("databaseId")));

        // <!ELEMENT update (#PCDATA | selectKey | include | trim | where | set |
        // foreach | choose | if | bind)*>
        // <!ATTLIST update
        // id CDATA #REQUIRED
        // parameterMap CDATA #IMPLIED
        // parameterType CDATA #IMPLIED
        // timeout CDATA #IMPLIED
        // flushCache (true|false) #IMPLIED
        // statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
        // keyProperty CDATA #IMPLIED
        // useGeneratedKeys (true|false) #IMPLIED
        // keyColumn CDATA #IMPLIED
        // databaseId CDATA #IMPLIED
        // lang CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("update",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("selectKey"),
                        new JDTDName("include"), new JDTDName("trim"), new JDTDName("where"), new JDTDName("set"),
                        new JDTDName("foreach"), new JDTDName("choose"), new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("id", JDTDDecl.REQUIRED), new JDTDAttribute("parameterMap"),
                new JDTDAttribute("parameterType"), new JDTDAttribute("timeout"),
                new JDTDAttribute("flushCache", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("statementType", new JDTDEnumeration("STATEMENT", "PREPARED", "CALLABLE")),
                new JDTDAttribute("keyProperty"),
                new JDTDAttribute("useGeneratedKeys", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("keyColumn"), new JDTDAttribute("databaseId"), new JDTDAttribute("lang")));

        // <!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose |
        // if | bind)*>
        // <!ATTLIST delete
        // id CDATA #REQUIRED
        // parameterMap CDATA #IMPLIED
        // parameterType CDATA #IMPLIED
        // timeout CDATA #IMPLIED
        // flushCache (true|false) #IMPLIED
        // statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
        // databaseId CDATA #IMPLIED
        // lang CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("delete",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("id", JDTDDecl.REQUIRED), new JDTDAttribute("parameterMap"),
                new JDTDAttribute("parameterType"), new JDTDAttribute("timeout"),
                new JDTDAttribute("flushCache", new JDTDEnumeration("true", "false")),
                new JDTDAttribute("statementType", new JDTDEnumeration("STATEMENT", "PREPARED", "CALLABLE")),
                new JDTDAttribute("databaseId"), new JDTDAttribute("lang")));

        // <!-- Dynamic -->

        // <!ELEMENT include (property+)?>
        // <!ATTLIST include
        // refid CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("include",
                new JDTDMixed(JDTDCardinal.OPTIONAL, new JDTDName("property", JDTDCardinal.ONEMANY)),
                new JDTDAttribute("refid", JDTDDecl.REQUIRED)));

        // <!ELEMENT bind EMPTY>
        // <!ATTLIST bind
        // name CDATA #REQUIRED
        // value CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("bind", empty, new JDTDAttribute("name", JDTDDecl.REQUIRED),
                new JDTDAttribute("value", JDTDDecl.REQUIRED)));

        // <!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if
        // | bind)*>
        // <!ATTLIST sql
        // id CDATA #REQUIRED
        // lang CDATA #IMPLIED
        // databaseId CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("sql",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("id", JDTDDecl.REQUIRED), new JDTDAttribute("lang"),
                new JDTDAttribute("databaseId")));

        // <!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose |
        // if | bind)*>
        // <!ATTLIST trim
        // prefix CDATA #IMPLIED
        // prefixOverrides CDATA #IMPLIED
        // suffix CDATA #IMPLIED
        // suffixOverrides CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("trim",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                "prefix", "prefixOverrides", "suffix", "suffixOverrides"));
        // <!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose |
        // if | bind)*>
        dtd.add(new JDTDElement("where",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind"))));
        // <!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if
        // | bind)*>
        dtd.add(new JDTDElement("set",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind"))));

        // <!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose
        // | if | bind)*>
        // <!ATTLIST foreach
        // collection CDATA #REQUIRED
        // item CDATA #IMPLIED
        // index CDATA #IMPLIED
        // open CDATA #IMPLIED
        // close CDATA #IMPLIED
        // separator CDATA #IMPLIED
        // >
        dtd.add(new JDTDElement("foreach",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("collection", JDTDDecl.REQUIRED), new JDTDAttribute("item"),
                new JDTDAttribute("index"), new JDTDAttribute("open"), new JDTDAttribute("close"),
                new JDTDAttribute("separator")));

        // <!ELEMENT choose (when* , otherwise?)>
        dtd.add(new JDTDElement("choose", new JDTDSequence(new JDTDName("when", JDTDCardinal.ZEROMANY),
                new JDTDName("otherwise", JDTDCardinal.OPTIONAL))));
        // <!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose |
        // if | bind)*>
        // <!ATTLIST when
        // test CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("when",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("test", JDTDDecl.REQUIRED)));
        // <!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach |
        // choose | if | bind)*>
        dtd.add(new JDTDElement("otherwise",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind"))));

        // <!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if
        // | bind)*>
        // <!ATTLIST if
        // test CDATA #REQUIRED
        // >
        dtd.add(new JDTDElement("if",
                new JDTDMixed(JDTDCardinal.ZEROMANY, new JDTDPCData(), new JDTDName("include"), new JDTDName("trim"),
                        new JDTDName("where"), new JDTDName("set"), new JDTDName("foreach"), new JDTDName("choose"),
                        new JDTDName("if"), new JDTDName("bind")),
                new JDTDAttribute("test", JDTDDecl.REQUIRED)));

        JXMLScriptBuilder.getNodeHandlerMap().forEach((name, handler) -> handler.accept(dtd));
        return dtd;
    }
}
